#include <QApplication>
#include <QTextCodec>


#include <cstdlib>
#include <memory>
#include <exception>
#include "google/protobuf/stubs/common.h"
#include <iostream>
#include <QSharedPointer>
#include <QTest>
#include <QString>
#include <QDir>
#include <iostream>

#include "mainwindow.h"
#include "global.h"
#include "entity/oms.pb.h"

#include "payload.h"
#include "wireformat.h"
#include "tcpconnection.h"
#include "testprinting.h"
#include "qslog.h"
#include "session.h"
#include "connectionerrorinfo.h"
#include "sessionstate.h"

using namespace apollo::core;
using namespace apollo::client;
using namespace apollo::message::entity;

int testSession(int argc, char *argv[])
{

	QApplication app(argc, argv);
	QsLogging::Logger& logger = QsLogging::Logger::instance();
	logger.setLoggingLevel(QsLogging::TraceLevel);
	const QString sLogPath(QDir(app.applicationDirPath()).filePath("logs/log.txt"));
	QsLogging::DestinationPtr fileDestination = QsLogging::DestinationFactory::MakeFileDestination(sLogPath,1024 * 1024 * 10,10);
	QsLogging::DestinationPtr debugDestination(
		QsLogging::DestinationFactory::MakeConsoleDestination() );
	logger.addDestination(debugDestination);
	logger.addDestination(fileDestination);

	QLOG_INFO() << "Starting App";

	QSharedPointer<ConnectionConfig> config(new ConnectionConfig);
	config->connectionName = QString("test");
	ConnectionConfig::UrlConfig url;
	url.ip = QString("127.0.0.1");
	url.port = 7979;
	url.name = QString("test url");
	url.schema = QString("tcp");

	config->urls.append(url);
	Session* session = NULL;

	TestPrinting printing;

	//{
		session = new Session(config);
		//QObject::connect(session, SIGNAL(finished()), session, SLOT(deleteLater()));
		QObject::connect(session, SIGNAL(finished()), &printing, SLOT(threadFinished()));
		session->moveToThread(session);
		session->start();
		QSharedPointer<LoginRequest> loginReq(new LoginRequest);
		loginReq->set_loginid("ww");
		loginReq->set_password("adfasfasdf");
		
		QObject::connect(session, SIGNAL(dataArrived(PayloadPtr)), &printing, SLOT(printPayload(PayloadPtr)));
		QObject::connect(session, SIGNAL(errorHappened(const ConnectionErrorInfo&)), &printing, SLOT(printError(const ConnectionErrorInfo&)));
		QObject::connect(session, SIGNAL(stateChanged(const SessionState&)), &printing, SLOT(printState(const SessionState&)));
		MessagePtr resp = session->connect(loginReq.dynamicCast<google::protobuf::Message>());

		QLOG_INFO()  << "response: " << resp->DebugString();

		//session->deleteLater();
		session->close();
		delete session;
		session = NULL;
	//}
	MainWindow mainWin;
	mainWin.show();
	//QLOG_INFO() << "login again";
    //session->connect(loginReq.dynamicCast<google::protobuf::Message>());
	int v = app.exec();
	//session->close();
	/*session->close();
		delete session;
		session = NULL;*/
	QLOG_INFO() << "Closed App";
	return v;
}

int testTcpConnection(int argc, char *argv[])
{
	QApplication app(argc, argv);




	QSharedPointer<ConnectionConfig> config(new ConnectionConfig);
	config->connectionName = QString("test");
	ConnectionConfig::UrlConfig url;
	url.ip = QString("127.0.0.1");
	url.port = 7979;
	url.name = QString("test url");
	url.schema = QString("tcp");

	config->urls.append(url);

	apollo::core::PayloadPtr payload(new apollo::core::Payload);
	payload->setMessageId(54321);
	payload->setMessageType(2);
	payload->setHeader(QString("dtotype=abc.de\ngroup=1"));
	char* body = new char[2];
	body[0] = 'A';
	body[1] = 'B';
	payload->setBodySize(2);
	payload->setBody(body);

	TcpConnection conn(config);
	TestPrinting printing;
	QObject::connect(&conn, SIGNAL(dataArrived(PayloadPtr)), &printing, SLOT(printPayload(PayloadPtr)));
	QObject::connect(&conn, SIGNAL(errorHappened(const ConnectionErrorInfo&)), &printing, SLOT(printError(const ConnectionErrorInfo&)));
	QObject::connect(&conn, SIGNAL(stateChanged(const SessionState&)), &printing, SLOT(printPayload(const SessionState&)));
	conn.connect(payload);
	conn.write(payload);
	//conn.disconnect();


	MainWindow mainWin;
	mainWin.show();

	return app.exec();
	char i;
	std::cin >> i;
	return 0;
}
void testWireFormat()
{

	apollo::core::WireFormat format;
	apollo::core::PayloadPtr payload(new apollo::core::Payload);
	payload->setMessageId(54321);
	payload->setMessageType(2);
	payload->setHeader(QString("dtotype=abc.de\ngroup=1"));
	char* body = new char[2];
	body[0] = 'A';
	body[1] = 'B';
	payload->setBodySize(2);
	payload->setBody(body);
	std::string p;
	format.encode(p, payload);

	std::cout << QTest::toHexRepresentation(p.c_str(), p.size()) << std::endl;

	apollo::core::PayloadPtr payload2(new apollo::core::Payload);
	quint16 checksum;
	format.decode(const_cast<char *>( p.c_str()) + 4, p.size() - 4, payload2, checksum);
	quint16 newchecksum = qChecksum(p.c_str(), p.size() - 2);

	if(checksum != newchecksum)
	{
		std::cout << "checksum is wrong" << std::endl;
	}

	if(payload->getMessageId() == payload2->getMessageId() 
		&& payload->getMessageType() == payload2->getMessageType()
		&& QString::compare( payload->getHeader(), payload2->getHeader()) == 0
		&& QString::compare(
		QString::fromLocal8Bit( payload->getBody(), payload->getBodySize())
		, QString::fromLocal8Bit(payload2->getBody(), payload2->getBodySize())) == 0
		)
	{
		std::cout << "wire format is ok" << std::endl;
	}
	else
	{
		std::cout << "wire format is wrong: body:" << (QString::compare(
			QString::fromLocal8Bit( payload->getBody(), payload->getBodySize())
			, QString::fromLocal8Bit(payload2->getBody(), payload2->getBodySize())) == 0)
			<< ", header: " << (QString::compare( payload->getHeader(), payload2->getHeader())== 0)
			<< ", messagId: " << (payload->getMessageId() == payload2->getMessageId() )
			<< ", messageType: " << (payload->getMessageType() == payload2->getMessageType())
			<< std::endl;
	}

}

int main(int argc, char *argv[])
{

	QTextCodec* utf8 = QTextCodec::codecForName("GB2312");

	QTextCodec::setCodecForCStrings(utf8);
	QTextCodec::setCodecForLocale(utf8);
	QTextCodec::setCodecForTr(utf8);
	GOOGLE_PROTOBUF_VERIFY_VERSION;

	qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
	qRegisterMetaType<QTcpSocket::SocketError>("QTcpSocket::SocketError");
	qRegisterMetaType<apollo::core::MessagePtr>("apollo::core::MessagePtr");
	qRegisterMetaType<apollo::core::PayloadPtr>("PayloadPtr");
	qRegisterMetaType<apollo::core::SessionState>("SessionState&");
	qRegisterMetaType<apollo::core::ConnectionErrorInfo>("ConnectionErrorInfo&");
	qRegisterMetaType<apollo::core::SessionState>("SessionState");
	qRegisterMetaType<apollo::core::ConnectionErrorInfo>("ConnectionErrorInfo");

	try
	{


		//testWireFormat();
		//testTcpConnection(argc, argv);
		testSession(argc, argv);
		return 0;


		container.initSystem();

		QApplication app(argc, argv);


		container.initLogSystem(app.applicationDirPath().append("/logs/log.txt"));
		QLOG_INFO() << "Starting Application.";

		Q_INIT_RESOURCE(mdi);


		//MainWindow mainWin;
		//mainWin.show();

		const QMetaObject *qmo = & apollo::client::MainWindow::staticMetaObject;
		QLOG_INFO() << "MainWindow class name: " << qmo->className();
		QObject* o = qmo->newInstance();
		MainWindow * w = dynamic_cast<MainWindow*>(o);
		w->show();
		int result = app.exec();
		delete o;
		//apollo::message::entity::MarketPrice mp;

		//delete ea.release();
		QLOG_INFO() << "Closing App.";
		//container.destroy();
		QLOG_INFO() << "Closed App.";
		return result;
	}
	catch(apollo::core::Exception &ex)
	{
		QLOG_ERROR()  << ex.getStackTraceString();
	}
	catch(std::exception &ex)
	{
		QLOG_ERROR() << ex.what();
		return 1;
	}
	catch(...)
	{
		QLOG_ERROR() << "Unhandled exception.";
		return 1;
	}
}



